Forthcoming language improvements

by Manu (modified: 2013 Sep 25)

The past 6 months we have been working on some new language improvements to simplify and improve the expressiveness of Eiffel.

The first improvement is the conditional expression. Very often, depending on some boolean value you expect either one result or another. Although one can express this, it is sometime tedious and/or not very readable. Using the new expression type, you would be able to write the following:

Result := if is_even then my_value // 2 else (my_value + 1) // 2 end

The other improvements are related to agents and to summarize those in 2 lines, we would like to go from the following agent call:

my_agent.call ([arg1, arg2])

to simply just

my_agent (arg1, arg2) .

To achieve this, two new additions to the language are necessary. The first one is simplifying feature calls when the last argument is a TUPLE by not having to write the manifest tuple. Instead you simply list all the elements of the tuple as remaining arguments. For example, given the following declaration:

my_routine (a_arg: INTEGER; a_coord: TUPLE [INTEGER, INTEGER])

you will now be able to write either of the following:

my_routine (1, [2, 3]) my_routine (1, 2, 3)

The second addition is the notion of parenthesis alias. Very similar to bracket alias, you can choose one routine of the current class to be the parenthesis alias. The direct benefit is that a call can be done without providing a name, just the arguments. For example, if in a class you have the following declaration:

my_routine alias "()" (arg1: INTEGER)

then you can all this routine using either of the following syntax:

a: A a.my_routine (1) a (1)

Merging those two new additions to routine call from ROUTINE or item from FUNCTION gives us the intended effect of simplifying agent calls.

At the moment, we are busy ironing out the details and the new validity rules. Those improvements will appear in this fall release and the spring release of 2014.

Let us know what you think in the comments below.

Happy Eiffeling!

Manu

Comments
  • Eric Bezault (10 years ago 26/9/2013)

    Changing the argument order in agents

    These are nice additions. What would be nice as well would be to be able to write 'agent f (?2, ?1)'. This would avoid to write a routineg' which calls f' with the arguments swapped and then write `agent g'.

    • Colin Adams (10 years ago 26/9/2013)

      I suggested this exact syntax some years ago, and Bertrand sounded sympathetic to it. I also suggested ?0 for the target, I think.

      It can also have other benefits, such as not needing to write a wrapper to repeat or drop one of the arguments.

  • Berend de Boer (10 years ago 26/9/2013)

    Useful additions, makes stuff simply a bit nicer to read.

  • Peter Gummer (10 years ago 26/9/2013)

    Love the conditional expression

    The new conditional expression existed in Algol fifty years ago, no?

    It reminds me of my old friend from the C language, the much-maligned ternary operator ... Result = is_even ? my_value / 2 : (my_value + 1) / 2; and strange as it may sound from an Eiffel enthusiast, I've always also been a big fan of the ternary operator in C and its imitators. (Well almost always: I clearly recall that it took me a year of solidly programming in C to feel really comfortable with it.) But once I had got used to it, I have always found that it improves code legibility in many common situations.

    It's a bit sad that the Eiffel version has to be terminated with the end keyword, because it adds a bit of clutter; but it has to be there, of course, in Eiffel. I don't see any alternative. It's probably a good thing for newbies anyway, avoiding the 12-month settling-in period I experienced with C's ternary operator. If C had used this more verbose syntax, I think I would have felt comfortable with it almost from day one.

    Looking at your example, I would find the urge irresistible to refactor it thus:

    Result := (if is_even then my_value else my_value + 1 end) // 2

    Still looks nice and legible to me!

    I can easily imagine that this new syntax will be very handy in preconditions, postconditions and invariants.

    One further language enhancement suggestion: recently the C# language added a ?? operator that I find extremely useful. An example is Result = x ?? new Foo(); which is equivalent to Result = x != null ? x : new Foo(); which in the new Eiffel syntax would be something along the lines of Result := if attached x then x else create {FOO} end. It may not look like much of an improvement, but imagine x replaced with a more complex expression and you can easily see how the code becomes much more readable. But maybe an enhancement like this wouldn't be very useful in void-safe Eiffel.

  • Peter Gummer (10 years ago 26/9/2013)

    The simplified agent call is great too

    I always find it hard to remember the fully explicit agent call syntax, so this will make them easier to write. Whether it's more legible is debatable ... yes it's more concise, but on the other hand it might introduce doubt about what's going on. But bracket aliases haven't caused much consternation as far as I'm aware, so I don't think this is going to be an issue.

    This is the normal way of calling method pointers in Delphi (which are more or less the same as Eiffel agents, but less flexible), which I worked with for many years, so it feels natural to me.

    I guess the new manifest tuple simplification will be handy for function calls too, but the parenthesis alias will only work for procedure calls. Function calls will still have the same old verbose syntax (slightly simplified), such as x := my_func.item (arg1, arg2), right? This seems a bit inconsistent. But I see at https://svn.eiffel.com/eiffelstudio/trunk/Src/library/base/elks/kernel/function.e that FUNCTION.item now has a bracket alias ... so I think this means that we should be able to write x := my_func [arg1, arg2].

    Does this have any implications for anonymous agents? I guess that (agent (i: INTEGER) do print (i + i) end).call ([1]) could be simplified to (agent (i: INTEGER) do print (i + i) end) (1).

    • Alexander Kogtenkov (10 years ago 19/11/2013)

      Parenthesis alias replaces bracket alias for functions

      The inconsistency of using parenthesis aliases for routine classes has been fixed by replacing the bracket alias (that works only for queries) for functions with the parenthesis alias. Now calls to agents use the same parenthesis notation be it a command or a query.

  • Bernd Schoeller (10 years ago 27/9/2013)

    If a is of type TUPLE[ANY],

    If a is of type TUPLE[ANY], and f is an agent taking a single argument arg: ANY, will the dynamic type of arg in call f.call(a) be of bound to ANY or TUPLE[ANY]?

    What happens with f.call([a]) ?

    Is there now a difference between f() and f, with respect to feature calls, or will () only work with at least one argument?

    • Bernd Schoeller (10 years ago 8/10/2013)

      Answers

      Darn - I really would have like some answers to these questions, but looks like we are now too far down the list of blog posts. Anybody can reference me to the standard describing these changes?

    • Alexander Kogtenkov (10 years ago 19/11/2013)

      Rules for tuple arguments

      Indeed, calling an agent without arguments and just accessing a reference to an agent object is not syntactically different if there are no arguments. Therefore the rule to convert trailing arguments to a tuple does not apply if there are no arguments at all.

      Similarly, if the last actual argument conforms to the last formal argument of a feature, the argument conversion does not take place. A side (yet important) effect of these rules is that all the existing code keeps running with the original semantics.

      To answer the questions:

      • The dynamic type of arg for f.call (a) will be the type of a, i.e. TUPLE [ANY].
      • The dynamic type of arg for f.call ([a]) will be TUPLE [TUPLE [ANY]] if a is of type TUPLE [ANY] as before.
      • There is no difference between f () and f.
      • The parenthesis alias can be used only with features taking at least one argument.
  • Howard Thomson (10 years ago 27/9/2013)

    Compile time constants

    Hi Manu,

    I have always been puzzled by the lack of a construct in Eiffel that provides, effectively, for a compile time constant.

    In C if I want to refer to a Gb of memory I can use #define (!) :

    '#define Gigabyte 1024*1024*1024

    whereas in Eiffel I either have to hand calculate the corresponding value and declare it as a constant:

    'giga_byte: INTEGER_64 = ##some-large-number-here##

    or else declare a once function, which compilers don't AFAIK convert into a run-time constant:

    'frozen giga_byte: INTEGER_64 once Result := 1024*1024*1024 end

    Of course this is a very simplistic example, but I still get the impression that there is room for improvement.

    Regards,

    Howard

  • Larry Rix (10 years ago 21/10/2013)

    TUPLE notation

    It appears at first blush that the suggested TUPLE notation waters down the Explicit Interfaces Principle, which is:

    Principle of Explicit Interfaces The principle of explicit interfaces states that whenever two modules A and B communicate, this must be obvious from the text of A or B or both.

    The way the notation works now, the API is explicit from the view of both A and B. Thus, changing the notation in B waters down that side or weakens it.

    I am concerned about implicit interfaces weakening readability and (as a colleague has suggested) opens the door for a new breed of errors. The fine folks at ETH have (most likely) thought through this process better than I, so my reaction may be more of "there-be-dragons". Still, it is a concern I presently have and would like to get feedback and time to think on it. I doubt we will be using it right out of the chute.

    Nevertheless -- the Result := if ____ then _____ else _____ end is a welcomed notation and very sensible and reasonable. It is also easy to read, which says, "YES, let's do this!"

    • Colin Adams (10 years ago 22/10/2013)

      I don't see any reduction in the principle of explicit interfaces - but then I don't consider TUPLE to be a module - it's just syntax.

  • Thomas Beale (10 years ago 24/11/2013)

    No need to sacrifice formal clarity

    I'm all for the conditional syntax; I really miss the surprisingly effective :? ternary operator from C.

    As for tuples, if you declare f (arg1: T; arg2: TUPLE [G, H, J]) then it should be required in my view that the call is done by: f (x, [y, z, w])

    because tuples are real things, and TUPLE is a meta-type in the Eiffel system, just like FUNCTION and ROUTINE.

    Allowing f (x, y, z, w) just obfuscates the code. This isn't academic: I have at least 4 manually declared TUPLE types in my code. The reader of the code needs to know that the argument is a tuple.

    As for my_agent.call ([x, y, z]) it could be relaxed, because we know that my_agent as been declared as a PROCEDURE, so there is no reason not to allow my_agent (x, y, z), since it can have its own syntax (it's a different kind of reference to a procedure than an inline declared procedure).